package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private VodService vodService;

    @Autowired
    private TrackStatMapper trackStatMapper;

    /**
     * 保存声音
     *
     * @param userId      用户ID
     * @param trackInfoVo 声音VO信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(Long userId, TrackInfoVo trackInfoVo) {
        //1.新增声音记录
        //1.1 将声音VO转为PO对象
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        //1.2 设置声音对象中：用户ID  来源  状态
        trackInfo.setUserId(userId);
        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        //1.2.1 根据专辑ID查询专辑信息得到包含声音数量
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        Assert.notNull(albumInfo, "专辑不存在！");
        //1.2.2 用于计算新增声音序号
        Integer includeTrackCount = albumInfo.getIncludeTrackCount();
        trackInfo.setOrderNum(includeTrackCount + 1);

        //1.3 远程调用腾讯云点播平台根据文件唯一标识获取音频文件详情
        TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
        if (trackMediaInfoVo != null) {
            // 音频文件时长
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
            // 音频文件类型
            trackInfo.setMediaType(trackMediaInfoVo.getType());
            // 音频文件大小
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        }
        //1.4 新增声音记录得到声音ID
        trackInfoMapper.insert(trackInfo);
        Long trackId = trackInfo.getId();

        //2.更新声音所属专辑包含声音数量
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);

        //3.新增声音统计记录
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_PLAY, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_COLLECT, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_PRAISE, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_COMMENT, 0);
    }

    /**
     * 保存声音统计信息
     *
     * @param trackId  声音ID
     * @param statType 统计类型 0701-播放量 0702-收藏量 0703-点赞量 0704-评论数
     * @param num      数值
     */
    @Override
    public void saveTrackStat(Long trackId, String statType, int num) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(num);
        trackStatMapper.insert(trackStat);
    }

    /**
     * 分页查询当前登录用户发布声音列表
     *
     * @param pageInfo       MP分页对象
     * @param trackInfoQuery 查询套件
     * @return MP分页对象
     */
    @Override
    public Page<TrackListVo> getUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
        return trackInfoMapper.getUserTrackPage(pageInfo, trackInfoQuery);
    }


    /**
     * 修改声音信息
     *
     * @param id 声音ID
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
        //1.根据声音ID查询声音信息
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        Assert.notNull(trackInfo, "声音不存在！");
        //2.判断音频信息是否发生变化
        String mediaFileIdBefore = trackInfo.getMediaFileId();
        String mediaFileIdAfter = trackInfoVo.getMediaFileId();


        //将用户提交声音拷贝到PO对象中
        BeanUtil.copyProperties(trackInfoVo, trackInfo);

        if (!mediaFileIdBefore.equals(mediaFileIdAfter)) {
            //3.调用腾讯云点播平台获取最新音频文件信息 更新 声音中音频信息
            TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(mediaFileIdAfter);
            trackInfo.setMediaDuration(BigDecimal.valueOf(mediaInfo.getDuration()));
            trackInfo.setMediaType(mediaInfo.getType());
            trackInfo.setMediaSize(mediaInfo.getSize());
            //4.调用腾讯云点播平台将旧文件进行删除-避免垃圾数据占用空间浪费资金
            log.info("+++++++++++++++++++++" + Thread.currentThread().getName());
            vodService.deleteMedia(mediaFileIdBefore);
        }
        //5.更新声音信息
        trackInfoMapper.updateById(trackInfo);
    }

    /**
     * 根据声音ID删除声音
     *
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfo(Long id) {
        //1.获取要被删除声音信息 得到 当前声音序号
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        Assert.notNull(trackInfo, "声音不存在！");

        //2.根据序号修改比当前声音序号大的声音序号
        Integer orderNum = trackInfo.getOrderNum();
        trackInfoMapper.updateOrderNum(trackInfo.getAlbumId(), orderNum);

        //3.删除声音记录
        trackInfoMapper.deleteById(id);

        //4.专辑包含声音数量减少
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);

        //5.调用腾讯点播平台将音频文件删除
        vodService.deleteMedia(trackInfo.getMediaFileId());

        //TODO 6 删除声音统计信息
        LambdaQueryWrapper<TrackStat> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackStat::getTrackId, id);
        trackStatMapper.delete(queryWrapper);
    }

    @Autowired
    private UserFeignClient userFeignClient;

    /**
     * 分页获取当前用户查看专辑的声音列表（动态判断付费标识）
     *
     * @param userId   用户ID
     * @param pageInfo 分页对象
     * @param albumId  专辑ID
     * @return 分页对象
     */
    @Override
    public Page<AlbumTrackListVo> getAlbumTrackPageByCurrUser(Long userId, Long albumId, Page<AlbumTrackListVo> pageInfo) {
        //1.分页专辑ID查询声音列表-默认声音付费标识为false（免费）
        pageInfo = trackInfoMapper.getAlbumTrackPageByCurrUser(pageInfo, albumId);
        //2.根据专辑ID查询专辑信息-得到专辑付费类型 0101-免费、0102-vip免费、0103-付费
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        Assert.notNull(albumInfo, "专辑{}不存在", albumId);
        String payType = albumInfo.getPayType();

        //3.处理未登录情况
        if (userId == null) {
            //3.1 判断专辑付费类型是否为：VIP免费(0102)、付费(0103)
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //3.2 遍历当前页声音列表-将免费试听以外声音付费标识设置true
                pageInfo.getRecords().stream().filter(albumTrackListVo -> {
                    //将声音序号大于专辑免费实体集数的声音获取到
                    return albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree();
                }).forEach(albumTrackListVo -> {
                    albumTrackListVo.setIsShowPaidMark(true);
                });
            }
        } else {
            //4.处理已登录情况
            Boolean isNeedCheckPayStatus = false;
            //4.1 远程调用用户服务获取用户信息得到用户身份 0:普通用户  1:VIP会员
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "当前用户：{}为空", userId);
            Integer isVip = userInfoVo.getIsVip();
            //4.2 处理付费类型为:VIP免费且当前用户为普通用户/VIP过期
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)) {
                if (isVip.intValue() == 0) {
                    //普通用户
                    isNeedCheckPayStatus = true;
                }
                if (isVip.intValue() == 1 && new Date().after(userInfoVo.getVipExpireTime())) {
                    //VIP过期
                    isNeedCheckPayStatus = true;
                }
            }
            //4.3 处理付费类型为 付费
            if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                isNeedCheckPayStatus = true;
            }
            //4.4 统一对需要进一步确认购买情况进行处理
            if (isNeedCheckPayStatus) {
                //4.4.1 获取本页中除了免费试听声音集合
                List<AlbumTrackListVo> trackListVoList = pageInfo.getRecords();
                trackListVoList = trackListVoList.stream()
                        //将免费试听声音排除掉
                        .filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree())
                        //收集声音VO集合
                        .collect(Collectors.toList());

                //4.4.2 当前页中需要检查购买情况声音ID集合
                List<Long> needCheckTrackIdList = trackListVoList
                        //将声音VO集合转为声音ID集合
                        .stream().map(AlbumTrackListVo::getTrackId)
                        .collect(Collectors.toList());
                //4.4.3 远程调用用户服务-查询本页中声音购买情况（将免费试听去掉）
                Map<Long, Integer> payStatusMap = userFeignClient.userIsPaidTrack(userId, albumId, needCheckTrackIdList).getData();
                //4.4.4 遍历本页声音列表(将免费试听去掉)，判断未购买将付费标识设置为true
                for (AlbumTrackListVo albumTrackListVo : trackListVoList) {
                    if (payStatusMap.get(albumTrackListVo.getTrackId()) == 0) {
                        //当前声音用户未购买，将付费标识设置true 页面中显示人民币付费按钮
                        albumTrackListVo.setIsShowPaidMark(true);
                    }
                }
            }
        }
        return pageInfo;
    }

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 更新声音及所属专辑统计信息
     *
     * @param mqVo
     */
    @Override
    public void updateTrackStat(TrackStatMqVo mqVo) {
        //1.确保消息幂等性（避免同一消息被MQ多次投递）
        String businessNo = mqVo.getBusinessNo();
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(businessNo, "", 1, TimeUnit.MINUTES);
        if (flag) {
            //说明第一次处理该业务消息
            //2.更新声音统计信息
            trackInfoMapper.updateStat(mqVo.getTrackId(), mqVo.getStatType(), mqVo.getCount());
            //3.更新专辑统计信息 专辑跟声音共有统计类型：播放、评论 当声音统计被更新所属的专辑才更新
            if (SystemConstant.TRACK_STAT_PLAY.equals(mqVo.getStatType())) {
                albumInfoMapper.updateStat(mqVo.getAlbumId(), SystemConstant.ALBUM_STAT_PLAY, mqVo.getCount());
            }
            if (SystemConstant.TRACK_STAT_COMMENT.equals(mqVo.getStatType())) {
                albumInfoMapper.updateStat(mqVo.getAlbumId(), SystemConstant.ALBUM_STAT_COMMENT, mqVo.getCount());
            }
        }

    }


    /**
     * 根据声音ID查询声音统计信息
     *
     * @param trackId
     * @return
     */
    @Override
    @GuiGuCache(prefix = "track:info:")
    public TrackStatVo getTrackStatVo(Long trackId) {
        return trackInfoMapper.getTrackStatVo(trackId);
    }


    /**
     * 动态构建当前用户分集购买列表
     *
     * @param trackId 选择“付费标识”声音ID
     * @return [{name:"本集",price:1,trackCount:1},{name:"后10集",price:10,trackCount:10},{name:"全集",price:17,trackCount:17}]
     */
    @Override
    public List<Map<String, Object>> getUserWaitBuyList(Long trackId) {
        //1.获取当前用户id
        Long userId = AuthContextHolder.getUserId();
        //2.根据"选择购买声音ID"查询声音信息-得到专辑ID、声音序号
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        BigDecimal price = albumInfo.getPrice();
        //3.根据专辑ID+声音序号条件查询所有大于指定序号声音列表
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        queryWrapper.ge(TrackInfo::getOrderNum, trackInfo.getOrderNum());
        List<TrackInfo> userWaitPayTrackList = trackInfoMapper.selectList(queryWrapper);
        //4.远程调用"用户服务"得到用户已购买声音ID集合
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();
        //5.如果存在已购买声音ID，将所有声音列表中已购买声音排除掉得到 “未购买声音列表”
        List<Map<String, Object>> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
            userWaitPayTrackList = userWaitPayTrackList.stream()
                    .filter(trackInfo1 -> !userPaidTrackIdList.contains(trackInfo1.getId()))
                    .collect(Collectors.toList());
        }
        //6.根据未购买声音列表长度动态购买分集购买列表
        int size = userWaitPayTrackList.size();
        //6.1 构建"本集"购买对象
        Map<String, Object> currMap = new HashMap<>();
        currMap.put("name", "本集");
        currMap.put("price", price);
        currMap.put("trackCount", 1);
        list.add(currMap);

        //6.2 如果代购买声音列表长度在N*10~(N+1)*10 区间展示 固定显示后 N*10 集合
        //6.3 如果待购买声音列表长度大于N*10集 显示 全集
        for (int i = 10; i <= 50; i += 10) {
            if (size > i) {
                // 长度大于N*10集 显示固定后N*10集
                Map<String, Object> map = new HashMap<>();
                map.put("name", "后" + i + "集");
                map.put("price", price.multiply(new BigDecimal(i)));
                map.put("trackCount", i);
                list.add(map);
            } else {
                //显示全集 精确集数
                Map<String, Object> map = new HashMap<>();
                map.put("name", "全集（" + size + "集)");
                map.put("price", price.multiply(new BigDecimal(size)));
                map.put("trackCount", size);
                list.add(map);
                break;
            }
        }
        return list;
    }

    /**
     * 查询当前用户待购买声音列表作为待结算商品列表
     *
     * @param trackId    选择购买声音ID
     * @param trackCount 购买集数
     * @return
     */
    @Override
    public List<TrackInfo> getUserWaitBuyTrackInfoList(Long trackId, Integer trackCount) {
        //1.根据声音ID查询声音信息得到声音所属专辑ID，序号
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();
        Integer orderNum = trackInfo.getOrderNum();

        //2.查询声音表中用户未购买过的后*条声音记录
        //2.1 获取用户已购买声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();
        //2.2 构建查询声音条件对象
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        //2.2.1 查询指定专辑的声音
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
            //2.2.2 如果存在用户已购买声音ID，将已购买声音排除掉
            queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
        }
        //2.2.3 声音序号大于当前购买声音序号
        queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
        //2.2.4 按照声音序号升序
        queryWrapper.orderByAsc(TrackInfo::getOrderNum);
        //2.2.5 指定用户购买指定集数数量
        queryWrapper.last("limit " + trackCount);
        //2.2.6 避免将声音播放地址直接返回
        queryWrapper.select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId);
        //2.3 查询待购买声音列表
        return trackInfoMapper.selectList(queryWrapper);
    }
}
